package activity

import (
	"bytes"
	"errors"
	"fmt"
	global2 "longmen/server/config/global"
	"longmen/server/internal/rpc/tencentim/chat"
	_const "longmen/server/pkg/common/helper/const"
	string2 "longmen/server/pkg/common/helper/string"
	time2 "longmen/server/pkg/common/helper/timehelper"
	utils2 "longmen/server/pkg/common/helper/util"
	"longmen/server/pkg/db/mysql"
	"longmen/server/pkg/db/mysql/models/models_main"
	"time"

	"github.com/jinzhu/copier"
	"gorm.io/gorm"

	"longmen/server/pkg/common/api_models"
	"longmen/server/pkg/db/mysql/db_main"
	"longmen/server/pkg/db/redis/cache"

	"github.com/labstack/echo/v4"
	"github.com/spf13/cast"
)

/*
*
* @author Aaron
* @description  Service层 根据任务ID获取任务奖励
* @createTime 11:08 2022/5/18
modify: Aug11 Enzo
*
*/
func getTaskRewardById(id uint8) (*api_models.TaskReward, error) {
	fName := "GetTaskRewardById"
	resp := &api_models.TaskReward{}
	TaskRewardList, err := db_main.GetTaskRewardById(id)
	if err != nil {
		return nil, fmt.Errorf("%s%s", utils2.Convert(fName), err.Error())
	}
	for _, reward := range TaskRewardList {
		switch _const.RewardType(reward.RewardType) {
		case _const.REWARD_TYPE_DIAMOND:
			resp.Diamond = reward.Quantity //奖励用户克拉
		case _const.REWARD_TYPE_EXP:
			resp.Exp = reward.Quantity //奖励用户经验
		case _const.REWARD_TYPE_GOLD:
			resp.Gold = reward.Quantity //奖励用户鹰钻
		case _const.REWARD_TYPE_GIFT:
			resp.Gift = append(resp.Gift, &api_models.Gift{
				Id:       reward.GiftId,
				Title:    reward.Title,
				Icon:     reward.Icon,
				Quantity: reward.Quantity,
			})
		case _const.REWARD_TYPE_NOBLE_EXPERIENCE_DAY:
			resp.NobleExpDay = reward.Quantity //奖励贵族卡
		case _const.REWARD_TYPE_PROP: //奖励道具
			resp.Props = append(resp.Props, &api_models.PropsReward{
				Props: api_models.Props{
					Id:   int(reward.PropsId),
					Name: reward.Title,
					Icon: reward.Icon,
				},
				Quantity: int(reward.Quantity),
			})
		case _const.REWARD_TYPE_LOTTERY_FREE_NUM:
			resp.LotteryFreeNum = reward.Quantity //奖励抽奖天数
		}
	}
	if len(resp.Gift) == 0 {
		resp.Gift = []*api_models.Gift{}
	}
	return resp, nil
}

func genTaskProfitList(reward *api_models.TaskReward, uid uint, oTask *api_models.TaskById) []*models_main.UserProfit {
	f_profit_node := func(coinCount int, coinType int) *models_main.UserProfit {
		return &models_main.UserProfit{
			Uid:         int(uid), //记录的主人
			FUid:        0,        //system
			ConsumeType: _const.PROFIT_TASK,
			Type:        _const.TYPE_PROFIT, //这是一个赚钱记录

			CoinCount: coinCount,
			Content: fmt.Sprintf("完成任务%s,收到奖励%.2f%s",
				oTask.Name, float64(coinCount)/100, utils2.GetCoinName(coinType)),

			Resid:      int(oTask.Id),
			Count:      coinCount,
			CreateTime: time.Now(),
			CoinType:   coinType,
			OrderNo:    utils2.GenOrderId("GT", 1),
			Liveid:     0,
		}
	}

	var profitLog []*models_main.UserProfit
	if reward.Diamond > 0 {
		profitLog = append(profitLog, f_profit_node(int(reward.Diamond), int(_const.CoinDiamond)))
	}
	if reward.Gold > 0 {
		profitLog = append(profitLog, f_profit_node(int(reward.Gold), int(_const.CoinGold)))
	}
	return profitLog
}

func genTaskNotice(reward *api_models.TaskReward, oTask *api_models.TaskById, uid uint) *models_main.Notice {
	var exp, diamond, vemon, gift, nobleExp, props, lotteryFreeNum string
	if reward.Exp != 0 {
		exp = fmt.Sprintf("经验值x%s、", cast.ToString(reward.Exp))
	}
	if reward.Gold != 0 {
		vemon = fmt.Sprintf("龙币x%.2f、", cast.ToFloat64(reward.Gold)/100)
	}
	if reward.Diamond != 0 {
		diamond = fmt.Sprintf("龙钻x%.2f。", cast.ToFloat64(reward.Diamond)/100)
	}
	if len(reward.Gift) != 0 {
		gift = fmt.Sprintf("礼物:")
		var bt bytes.Buffer
		bt.WriteString(gift)
		for _, g := range reward.Gift {
			bt.WriteString(g.Title)
			bt.WriteString("x")
			bt.WriteString(cast.ToString(g.Quantity))
			bt.WriteString("、")
		}
		gift = bt.String()
	}
	if reward.NobleExpDay != 0 {
		nobleExp = fmt.Sprintf("贵族体验x%s天。", cast.ToString(reward.NobleExpDay))
	}
	if len(reward.Props) != 0 {
		props = fmt.Sprintf("道具:")
		var bt bytes.Buffer
		bt.WriteString(props)
		for _, g := range reward.Props {
			bt.WriteString(g.Name)
			bt.WriteString("x")
			bt.WriteString(cast.ToString(g.Quantity))
			bt.WriteString("、")
		}
		props = bt.String()
	}
	if reward.LotteryFreeNum != 0 {
		lotteryFreeNum = fmt.Sprintf("免费抽奖x%s次。", cast.ToString(reward.LotteryFreeNum))
	}
	content := fmt.Sprintf("恭喜您已完成：%s任务(ID:%d)，奖励如下：%s%s%s%s%s%s%s", oTask.Name, oTask.Id, exp, vemon, diamond, gift, nobleExp, props, lotteryFreeNum)
	runes := []rune(content)
	if runes[len(runes)-1] == '、' { //对礼物的名称进行修减
		content = string(runes[:len(runes)-1])
	}
	return &models_main.Notice{
		UserId:  uid,
		Content: content,
	}
}

/*
在这里处理任务事件
*/
func processTaskEvent(ut *models_main.UserTask) error {
	//fName := "6ProcessTaskEvent9"
	oT, err := db_main.GetTaskById(ut.TaskId)
	if err != nil {
		global2.LogAndErrorf(err.Error())
	}
	bNeedToCreate := true
	if ut.Id != 0 {
		bNeedToCreate = false
	}
	return receiveTaskReward(oT, ut, bNeedToCreate) //发奖，事务，同时标记为已付

}

/**
* @author Aaron
* @description
* @createTime 10:36 2022/5/19
* Aug11 重写 by Enzo 增加profit记录，每日任务会变多条记录
**/
func receiveTaskReward(oTask *api_models.TaskById, oUserTask *models_main.UserTask, needToCreate bool) error {
	global2.Log.Debugf("%v%v%v", oTask, oUserTask, needToCreate)
	fName := "ReceiveTaskReward"
	reward, err := getTaskRewardById(uint8(oTask.Id)) //获取奖励
	if err != nil {
		return fmt.Errorf("%s%s", utils2.Convert(fName), err.Error())
	}
	if oUserTask.Stage >= oTask.Frequency { //任务完成
		oUserTask.IsReceived = 1
	} else {
		//没有完成，累加而已
		if needToCreate {
			oUserTask.CreatedAt = time.Now()
			oUserTask.Id, err = mysql.InsertXAny(oUserTask, fName) //------------DB:这里新建
		} else {
			err = mysql.UpdateXAnyParam(nil, []string{"stage", "is_received", "task_time"}, oUserTask, fName) //------------DB:这里update
			if err != nil {
				return err
			}
		}
		cache.SetUserTask(oUserTask, int(oUserTask.TaskId), int(oUserTask.Uid), time.Hour*24)
		return err
	}

	tx := global2.GetX().Begin() //------------------DB:事务开始-----------------//
	defer func() {
		if r := recover(); r != nil {
			tx.Rollback()
		}
	}()
	if oTask == nil { //这个oTask是上文顺便传入的，如果没有的话，就重新查一下
		oTask, err = db_main.GetTaskById(oUserTask.TaskId)
		if err != nil {
			return fmt.Errorf("%s%s", utils2.Convert(fName), err.Error())
		}
	}

	user, err := mysql.GetUserParam(echo.Map{"id": oUserTask.Uid})
	if err != nil {
		return fmt.Errorf("%s%s", utils2.Convert(fName), err.Error())
	}
	userUpdateColumns := []string{"gold", "point", "diamond"}

	//活动奖励发放
	err = saveGift(tx, user.Id, reward.Gift)
	if err != nil {
		tx.Rollback()
		return fmt.Errorf("%s%s", utils2.Convert(fName), err.Error())
	}

	//处理签到奖励逻辑
	if oTask.Type == uint8(_const.CHECK_IN_TASK) {
		//贵族卡
		if reward.NobleExpDay > 0 {
			/**
			需求:
			a）用户未开启贵族盲盒，用户开启盲盒后自动叠加体验天数；
			b）用户已经开启盲盒未到期，在现有的贵族盲盒上叠加天数；
			c）用户已经开启盲盒并已过期，在用户最近一次开启的贵族盲盒类型上叠加天数；
			d）用户已开通贵族，在当前贵族上叠加天数；
			4·奖励礼物
			*/
			err = nobleExperience(tx, int(oUserTask.Uid), int(reward.NobleExpDay))
			if err != nil {
				tx.Rollback()
				return fmt.Errorf("%s%s", utils2.Convert(fName), err.Error())
			}
		}

		//道具奖励
		if len(reward.Props) > 0 {
			err = saveProps(tx, reward.Props, user.Id)
			if err != nil {
				tx.Rollback()
				return fmt.Errorf("%s%s", utils2.Convert(fName), err.Error())
			}
		}

		//免费抽奖次数
		if reward.LotteryFreeNum > 0 {
			err = lotteryFreeNum(tx, reward, oUserTask)
			if err != nil {
				tx.Rollback()
				return fmt.Errorf("%s%s", utils2.Convert(fName), err.Error())
			}
		}
	}

	user.Gold += int(reward.Gold)
	user.Point += int(reward.Exp)
	user.Diamond += int(reward.Diamond)
	err = db_main.UpdateUserByTransactions(tx, userUpdateColumns, user)
	if err != nil {
		return fmt.Errorf("%s%s", utils2.Convert(fName), err.Error())
	}
	profitLogs := genTaskProfitList(reward, oUserTask.Uid, oTask)
	err = db_main.InsertUserProfitByTransactions(tx, profitLogs)
	if err != nil {
		return fmt.Errorf("%s%s", utils2.Convert(fName), err.Error())
	}
	oUserTask.IsReceived = 1

	var userTaskId int
	if needToCreate == false {
		err = mysql.UpdateXAnyParam(nil, []string{"stage", "is_received", "task_time"}, oUserTask, fName) //------------DB:这里update
	} else {
		userTaskId, err = mysql.InsertAnyByTx(tx, oUserTask, fName) //------------DB:这里新建
		oUserTask.Id = userTaskId

	}
	if err != nil {
		return fmt.Errorf("%s%s", utils2.Convert(fName), err.Error())
	}
	notice := genTaskNotice(reward, oTask, oUserTask.Uid)
	err = db_main.InsertNoticeByTx(tx, notice) //领取任务的消息通知
	if err != nil {
		return fmt.Errorf("%s%s", utils2.Convert(fName), err.Error())
	}
	if err := tx.Commit().Error; err != nil { //------------------事务提交-------------------
		return fmt.Errorf("%s %s", utils2.Convert(fName), err.Error())
	}
	cache.SetUserTask(oUserTask, int(oUserTask.TaskId), int(oUserTask.Uid), 24*time.Hour)

	return nil
}

func saveGift(tx *gorm.DB, uid int, rewardGift []*api_models.Gift) error {
	var giftIds []int
	for _, gift := range rewardGift {
		giftIds = append(giftIds, int(gift.Id))
	}

	gifts, err := db_main.GetGiftsParam(
		map[string]interface{}{"id": map[string]interface{}{"in": giftIds}}, "id asc")
	if err != nil {
		return err
	}
	giftMp := map[int]*models_main.Gift{}
	for k, v := range gifts {
		giftMp[v.Id] = gifts[k]
	}

	for _, v := range rewardGift {
		gift, ok := giftMp[int(v.Id)]
		if !ok {
			continue
		}
		if gift.Type != 2 { //活动礼品
			continue
		}
		err = db_main.SaveUserItem(
			tx, 0, uid, int(v.Id),
			2, int(v.Quantity), v.Title,
			v.Title, 999*24*60*60)
		if err != nil {
			return err
		}
	}
	return nil
}

func saveProps(tx *gorm.DB, props []*api_models.PropsReward, uid int) error {
	noble, err := getMyNoble(uid)
	if err != nil {
		return err
	}
	for _, prop := range props {
		item, err := db_main.GetPropsINFO(prop.Id)
		if err != nil {
			return err
		}
		err = db_main.SaveUserItem(tx, noble.Id, uid, prop.Id, item.Type, prop.Quantity, item.Name, item.Remark, 999*24*60*60)
		if err != nil {
			return err
		}
	}
	return nil
}

func lotteryFreeNum(tx *gorm.DB, reward *api_models.TaskReward, oUserTask *models_main.UserTask) error {
	fName := "LotteryFreeNum"
	if reward.LotteryFreeNum > 0 {
		var userCheckin *models_main.UserCheckin
		userCheckin, err := db_main.GetUserCheckInByUid(int(oUserTask.Uid))
		if err != nil {
			return fmt.Errorf("%s%s", utils2.Convert(fName), err.Error())
		}
		if userCheckin == nil {
			userCheckin = &models_main.UserCheckin{
				Uid:     int(oUserTask.Uid),
				DrawNum: int(reward.LotteryFreeNum),
			}
			err = db_main.InsertUserCheckinByTx(tx, userCheckin)
			if err != nil {
				return fmt.Errorf("%s%s", utils2.Convert(fName), err.Error())
			}
			return nil
		}
		userCheckin.DrawNum += int(reward.LotteryFreeNum)
		return db_main.UpdateUserCheckinByTransactions(tx, []string{"draw_num"}, userCheckin)
	}
	return nil
}

func nobleExperience(tx *gorm.DB, uid int, nobleExpDay int) error {
	fName := "NobleExperience"

	var (
		err       error
		vipLen    = 0
		expTime   = time.Now().Unix() + int64(nobleExpDay)*24*3600 - 1
		noble     *models_main.NobleNew
		nobleList []*models_main.NobleNew
		from      = 2
	)

	nr, err := getMyNoble(uid)
	if err != nil {
		return err
	}

	// 1.获取等级列表
	nobleList, err = db_main.GetNobleList()
	if vipLen = len(nobleList); err != nil {
		return fmt.Errorf("%s %s", utils2.Convert(fName), err.Error())
	}
	if vipLen == 0 {
		return errors.New("系统未配置VIP等级")
	}

	// 2.随机取一个VIP
	idx := string2.RandInt(0, vipLen)
	noble = nobleList[idx]
	if nr.Id > 0 {
		expTime = nr.Expiration*24*3600 + expTime
		noble, _ = db_main.GetNobleById(uint8(nr.Id))
		from = 1
	}
	// noblePrivilege, err := repository.GetNoblePrivilege(noble.Id)
	if err != nil {
		errors.New("系统未配置VIP等级信息")
	}
	// 3.发放礼物道具
	//坐骑
	mount, err := db_main.GetPropsINFO(int(noble.PropsID))
	if err != nil {
		return errors.New("获取贵族座骑信息失败")
	}
	err = db_main.SaveUserItem(tx, int(noble.Id), uid, mount.Id, mount.Type, 1, mount.Name, mount.Remark, int64(nobleExpDay*24*60*60))
	if err != nil {
		return err
	}

	// 4.写入一条等级关联记录
	if err = db_main.InsertNobleRecordByTx(tx, &models_main.NobleRecord{
		NobleId:    noble.Id,  //贵族ID
		Uid:        uint(uid), //用户ID
		Expiration: expTime,   //到期时间戳
		AnchorId:   uint(nr.AnchorID),
		From:       from,
		CreateTime: time2.GetTimeStamp(1),
	}); err != nil {
		return fmt.Errorf("%s %s", utils2.Convert(fName), err.Error())
	}

	chat.SetClientUserVip(uid, int(noble.Id))
	return nil
}

func getMyNoble(uid int) (*api_models.GetNobleByIdResp, error) {
	record, err := db_main.GetNobleRecordByUid(uid)
	if err != nil {
		return nil, err
	}
	resp := &api_models.GetNobleByIdResp{}
	if record.Id > 0 {
		resp.Expiration = int64(time2.GetLeftDays(record.Expiration))
		nobleInfo, err := cache.GetNoble(uint8(record.NobleId))

		if err != nil {
			return resp, err
		}
		resp.From = record.From
		resp.AnchorID = int64(record.AnchorId)
		err = copier.Copy(resp, nobleInfo)
		if err != nil {
			return nil, err
		}
	}
	return resp, nil
}
